package us.icitap.commandcenter.business.ejb;

import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import javax.annotation.Resource;
import javax.ejb.EJB;
import javax.ejb.Schedule;
import javax.ejb.Stateless;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.MessageProducer;
import javax.jms.ObjectMessage;
import javax.jms.Session;

import com.vaadin.server.StreamResource;
import com.vaadin.server.StreamResource.StreamSource;

import us.icitap.commandcenter.business.AppUser;
import us.icitap.commandcenter.business.CallActionEnum;
import us.icitap.commandcenter.business.CallRolesEnum;
import us.icitap.commandcenter.business.DailyReportPdf;
import us.icitap.commandcenter.business.ExcelCallDocument;
import us.icitap.commandcenter.business.PdfCallDocument;
import us.icitap.commandcenter.business.ejb.persistence.Call;
import us.icitap.commandcenter.business.ejb.persistence.CallStatusEnum;
import us.icitap.commandcenter.business.ejb.persistence.CommandCenter;
import us.icitap.commandcenter.business.ejb.persistence.Dispatcher;
import us.icitap.commandcenter.ui.dispatcher.CallMessage;
import us.icitap.commandcenter.utils.DailyReportExcel;
import us.icitap.commandcenter.utils.TPUtil;
import us.icitap.commandcenter.utils.Utils;
import us.icitap.dao.Expression;
import us.icitap.dao.Operator;
import us.icitap.timsportal.business.SimpleUser;
import us.icitap.timsportal.ejb.remote.PortalBeanRemote;
import us.icitap.timsportal.entity.RoleEnum;
import us.icitap.timsportal.entity.User;
import us.icitap.utils.AppDataSource;
import us.icitap.utils.AppExcel;
import us.icitap.utils.AppMail;
import us.icitap.utils.AppPdf;
import us.icitap.utils.Result;


@Stateless
public class AppStatelessBean implements IAppStatelessBean {

	@Resource(name = "jms/TestConnectionFactory")
	public  ConnectionFactory connectionFactory;
	
	@EJB
	ICallPersistBean callPersistBean;

//	@EJB(lookup="")
//	PortalBeanRemote portalBeanRemote;

	@Override
	public ICallPersistBean getCallPersistBean() {
		return callPersistBean;
	}


	@Override
	public Result<AppUser> login(SimpleUser u){
		
		AppUser user = new AppUser();

		user.setUsername(u.getUsername());
		user.setLoginDate(new Date());
		
		System.out.println("AppStatelessBean.login() "+u.getBaseStructure());
		System.out.println("AppStatelessBean.login() "+u.getBaseStructure().getId());
		
		Result<CommandCenter> result = callPersistBean.findEntityById(u.getBaseStructure().getId(), CommandCenter.class);
		if(result.isSucceeded()){
			user.setCommandCenter(result.getObject());
			return new Result<AppUser>(user, true, null);
		}
		else
			return new Result<AppUser>(null, false, result.getMessage());
	}
	
	@Override
	public Result<AppUser> devLogin(){
		
		AppUser user = new AppUser();

		user.setUsername("APP_USER");
		user.setLoginDate(new Date());
	
		Result<CommandCenter> result = callPersistBean.findEntityById(139, CommandCenter.class);
		System.out.println(result.getObject());
		if(result.isSucceeded()){
			user.setCommandCenter(result.getObject());
			return new Result<AppUser>(user, true, null);
		}
		else
			return new Result<AppUser>(null, false, result.getMessage());
	}
	

	@Override
	public void sendMessage(Call call, CallActionEnum callActionEnum, boolean sendToOperator, boolean sendToDispatcher, boolean sendToSupervisor){ 
		MessageProducer messageProducer;
		MessageProducer msgProdSupervisor;
		MessageProducer msgProdOperator;
	  
	    try {
	    	
	    	Connection connection = connectionFactory.createConnection();
	        Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
	        
	        CallMessage callMessage;
	        ObjectMessage objectMessage;
	        
	        if(CallActionEnum.CHANGE_DISPATCHER.equals(callActionEnum)){
	        	 callMessage = new CallMessage(call, CallActionEnum.REMOVE_CALL);
		         objectMessage = session.createObjectMessage();
		         objectMessage.setObject(callMessage);
		         
		         Destination dest = session.createTopic("DISP" + call.getPreviousDispatcher().getId().toString());
		 	     messageProducer = session.createProducer(dest);
		 	     messageProducer.send(objectMessage);       
			     messageProducer.close();
	        }
	        
	       
	         callMessage = new CallMessage(call, callActionEnum);
	       
	         objectMessage = session.createObjectMessage();
	         objectMessage.setObject(callMessage);
	        
	       
	        
	        if(sendToOperator){
	        	 Destination destOperator = session.createTopic("OPERATOR" + call.getRecordUser());
	        	 msgProdOperator = session.createProducer(destOperator);
	        	 msgProdOperator.send(objectMessage);
	        	 msgProdOperator.close();
	        }
	        
	        if(sendToDispatcher){
	        	Destination dest = session.createTopic("DISP" + call.getMainDispatcher().getId().toString());
	 	        messageProducer = session.createProducer(dest);
	 	        messageProducer.send(objectMessage);       
		        messageProducer.close();
	        }
	        
	        if(sendToSupervisor){
	        	Destination destSupervisor = session.createTopic("SUPERVISOR" + call.getCommandCenter().getId());
	 	        msgProdSupervisor = session.createProducer(destSupervisor);
	 	        msgProdSupervisor.send(objectMessage);
		        msgProdSupervisor.close();
	        }
	        
	       
 
	        session.close();
	        connection.close();
	        
	      
		} catch (Exception e) {
			e.printStackTrace();
			System.err.println(e.getMessage());
		}
	}
	

	
	
	@Override
	public Result<List<Call>> getOperatorCallList(String username){
		List<Expression> exp = new ArrayList<Expression>();
		
		Date[] shiftLimits = Utils.ShiftLimits();
		exp.add(new Expression("recordUser", username, Operator.EQ));
		exp.add(new Expression("recordDate", shiftLimits[0], Operator.GREATER_EQ_THAN));
		exp.add(new Expression("recordDate", shiftLimits[1], Operator.LOWER_THAN ));
		exp.add(new Expression("baseCall", null, Operator.IS));
		
		Result<List<Call>> result = callPersistBean.findByCriteria(exp, null, null, Call.class);
		
		return result;
	}
	
	@Override
	public Result<List<Call>> getNewCallListForSupervisor(CommandCenter commandCenter) {
		List<Expression> expNewCall = new ArrayList<Expression>();

		expNewCall.add(new Expression("commandCenter", commandCenter, Operator.EQ));
		expNewCall.add(new Expression("actualStatus", CallStatusEnum.N, Operator.EQ));
		expNewCall.add(new Expression("forSupervisor", true, Operator.EQ));
		expNewCall.add(new Expression("baseCall", null, Operator.IS));
		
		Result<List<Call>> result = callPersistBean.findByCriteria(expNewCall, null, null, Call.class);
		
		return result;
	}
	
	@Override
	public Result<List<Call>> getInitialNewCallList(CommandCenter commandCenter) {
		List<Expression> expNewCall = new ArrayList<Expression>();


		expNewCall.add(new Expression("commandCenter", commandCenter, Operator.EQ));	
		expNewCall.add(new Expression("actualStatus", CallStatusEnum.N, Operator.EQ));
		expNewCall.add(new Expression("forSupervisor", true, Operator.NOT_EQ));
		expNewCall.add(new Expression("baseCall", null, Operator.IS));
		
		Result<List<Call>> result = callPersistBean.findByCriteria(expNewCall, null, null, Call.class);
		
		return result;
	}
	
	@Override
	public Result<List<Call>> getInitialNewCallList(List<Dispatcher> dispatchers) {
		List<Expression> expNewCall = new ArrayList<Expression>();

		if(dispatchers.size() > 1)
			expNewCall.add(new Expression("mainDispatcher", dispatchers, Operator.EQ));	
		else
			expNewCall.add(new Expression("mainDispatcher", dispatchers.get(0), Operator.EQ));
		


		expNewCall.add(new Expression("actualStatus", CallStatusEnum.N, Operator.EQ));
		expNewCall.add(new Expression("forSupervisor", true, Operator.NOT_EQ));
		expNewCall.add(new Expression("baseCall", null, Operator.IS));
		Result<List<Call>> result = callPersistBean.findByCriteria(expNewCall, null, null, Call.class);
		
		return result;
	}
	
	@Override
	public Result<List<Call>> getWvCallList(List<Dispatcher> dispatchers) {
		List<Expression> expWvCall = new ArrayList<Expression>();
		if(dispatchers.size() > 1)
			expWvCall.add(new Expression("mainDispatcher", dispatchers, Operator.EQ));
		else
			expWvCall.add(new Expression("mainDispatcher", dispatchers.get(0), Operator.EQ));
		
		expWvCall.add(new Expression("actualStatus", CallStatusEnum.WV, Operator.EQ));
		expWvCall.add(new Expression("baseCall", null, Operator.IS));
		
		Result<List<Call>> result = callPersistBean.findByCriteria(expWvCall, null, null, Call.class);
		
		return result;
		
	}
	
	@Override
	public Result<List<Call>> getClosedCallList(List<Dispatcher> dispatchers) {
		List<Expression> expCloseCall = new ArrayList<Expression>();
		
		
		if(dispatchers.size() > 1){
			
			expCloseCall.add(new Expression("mainDispatcher", dispatchers, Operator.EQ));
		}
			
		else{
			
			expCloseCall.add(new Expression("mainDispatcher", dispatchers, Operator.EQ));
		}
			
		expCloseCall.add(new Expression("actualStatus", CallStatusEnum.V, Operator.EQ));
		expCloseCall.add(new Expression("baseCall", null, Operator.IS));
		
		Date[] shiftLimits = Utils.AllDayShiftLimits(new Date());
		
		expCloseCall.add(new Expression("verificationDate", shiftLimits[0], Operator.GREATER_EQ_THAN));
		expCloseCall.add(new Expression("verificationDate", shiftLimits[1], Operator.LOWER_THAN));
		Result<List<Call>> result = callPersistBean.findByCriteria(expCloseCall, null, null, Call.class);
		return result;
	}
	

	@Override
	public Result<Call> startVerification(Call call){
		Result<Call> result = callPersistBean.saveEntity(call);
		if(result.isSucceeded()){
			this.sendMessage(call, CallActionEnum.START_VERIFICATION, false,false,true);	
		}
		return result;
	
	}
	
	@Override
	public Result<Call> updateCallInformation(Call call, CallRolesEnum callRolesEnum){
		Result<Call> result = callPersistBean.saveEntity(call);
		if(result.isSucceeded()){
			if(CallRolesEnum.OPERATOR.equals(callRolesEnum))
				this.sendMessage(call, CallActionEnum.UPDATE_CALL, false, true, true);	
			else if(CallRolesEnum.DISPATCHER.equals(callRolesEnum))
				this.sendMessage(call, CallActionEnum.UPDATE_CALL, false, false, true);	
			else if(CallRolesEnum.SUPERVISOR.equals(callRolesEnum))
				this.sendMessage(call, CallActionEnum.UPDATE_CALL, false, true, false);	
				
		}
		return result;	
	}
	
	
	@Override
	public Result<Call> closeVerification(Call call, CallRolesEnum callRolesEnum){
		
		try {
			if(!call.isFalseCall() && call.getUniqueNumber() != null){
				String un = callPersistBean.generateUNString();
				call.getUniqueNumber().setUniqueNumber(un);
			}
			
			Result<Call> result = callPersistBean.saveEntity(call);
			if(result.isSucceeded() ){
				if(callRolesEnum.equals(CallRolesEnum.DISPATCHER))
					this.sendMessage(call, CallActionEnum.CLOSE_VERIFICATION, true, false,true);
		
			}
			return result;
		} catch (Exception e) {
			return new Result<Call>(null, false, e.getMessage());
		}
		
	}
	
	@Override
	public Result<Call> generateUniqueNumber(Call call){
		Result<Call> result = callPersistBean.generateUniqueNumber(call);
		if(result.isSucceeded()){
			this.sendMessage(call, CallActionEnum.GENERATE_UN, false, false, true);
			
		}
		return result;
	}
	
	@Override
	public Result<Call> addNewCall(Call call, CallRolesEnum callRolesEnum){
		Result<Call> result = callPersistBean.createCall(call);
		if(result.isSucceeded()){
			boolean sendToDispatcher = true;
			if(call.isForSupervisor() || CallRolesEnum.DISPATCHER.equals(callRolesEnum)) 
				sendToDispatcher = false;
			
			this.sendMessage(result.getObject(), CallActionEnum.NEW_CALL, false, sendToDispatcher, true);
		}
		return result;	
	}
	
	@Override
	public ConnectionFactory getConnectionFactory(){
		return connectionFactory;
	}


	@Override
	public Result<List<Dispatcher>> getDispatchers(CommandCenter cc) {
		try {
			List<Expression> exps = new ArrayList<Expression>();
			exps.add(new Expression("commandCenter", cc, Operator.EQ));
			exps.add(new Expression("status", true, Operator.EQ));
		
			return callPersistBean.findByCriteria(exps, null, null, Dispatcher.class);
		} catch (Exception e) {
			return new Result<List<Dispatcher>>(null, false, e.getMessage());
			// TODO: handle exception
		}
	}

	
	@Schedule(hour="8")
	@Override
	public void sendDailyCallReport(){
		try {

//			getlist of user to whom send the report (get email)
			
//			query DB for all calls of the past 24 hours
			Date[] limits = Utils.AllDayShiftLimits(new Date());
			
			List<Expression> exps = new ArrayList<Expression>();
			exps.add(new Expression("recordDate", limits[0], Operator.GREATER_EQ_THAN));
			exps.add(new Expression("recordDate", limits[1], Operator.LOWER_THAN));
			
			Result<List<Call>> result = callPersistBean.findByCriteria(exps, null, "o.recordDate", Call.class);
			System.out.println(result.isSucceeded());
			
			if(result.isSucceeded()){
				System.out.println(result.getObject().size());
//				generate PDF file with the call information
	
				AppDataSource appPdf = new DailyReportPdf(result.getObject(), limits);
				
				List<User> userList = TPUtil.portalBeanRemote.getUserByRole("SUPERVISOR");
				if(userList != null && !userList.isEmpty()){
					for(User u: userList){
						if(u.getEmail()==null || u.getEmail()=="")
							userList.remove(u);
					}				
					String[] emailList = new String[userList.size()];
					Iterator<User> iter = userList.iterator();
					while(iter.hasNext()){
						User u = iter.next();
						emailList[userList.indexOf(u)] = u.getEmail();
					}
					
					AppMail appMail = new AppMail(emailList, "Komunikata ditore e telefonatave " + limits[0] + " - " + limits[1], "salla.operative@asp.gov.al", appPdf);
					appMail.postMail();
				}
				
			}
			else{
				AppMail appMail = new AppMail("klaudetamertiku@gmail.com", "Komunikata ditore e telefonatave " + limits[0] + " - " + limits[1], "salla.operative@asp.gov.al", null);
				appMail.setMessage("Ka ndodhur nje gabim ne procesin e dergimit automatik te komunikates ditore te telefonatave. \n"+
				"Sistemi ka rregjistruar kete gabim: \n" + 
						result.getMessage());
				appMail.postMail();
				System.out.println(result.getMessage());
			}

		} catch (Exception e) {
			e.printStackTrace();
		}	
		
	}
	
	@Override
	public Result<StreamSource> generateExcelFromList(List<Call> callList){
		try {
			ExcelCallDocument excelCallDocument = new ExcelCallDocument(callList);
			return new Result<StreamSource>(excelCallDocument, true, null);
		} catch (Exception e) {
			return new Result<StreamSource>(null, false, e.getMessage());
		}
	}
	
	
	@Override
	public Result<StreamSource> generateCallPdf(Call call, String printingUser){
		try {
			PdfCallDocument pdfCallDocument = new PdfCallDocument(call, printingUser,"" );
			return new Result<StreamSource>(pdfCallDocument, true, null);
		} catch (Exception e) {
			return new Result<StreamSource>(null, false, e.getMessage());
		}
	}


	@Override
	public Result<Call> changeDispatcher(Call call, CallRolesEnum callRolesEnum){
		Result<Call> result = callPersistBean.saveEntity(call);
		if(result.isSucceeded()){
			if(CallRolesEnum.OPERATOR.equals(callRolesEnum))
				this.sendMessage(call, CallActionEnum.CHANGE_DISPATCHER, false, true, true);	
			else if(CallRolesEnum.DISPATCHER.equals(callRolesEnum))
				this.sendMessage(call, CallActionEnum.CHANGE_DISPATCHER, true, true, true);	
			else if(CallRolesEnum.SUPERVISOR.equals(callRolesEnum))
				this.sendMessage(call, CallActionEnum.CHANGE_DISPATCHER, true, true, false);	
				
		}
		return result;	
	}
	
	
}

